﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace SmartMathLibrary
{
    /// <summary>
    /// This class provides methods to map a linear function.
    /// </summary>
    [Serializable]
    public class LinearFunction : ILinearFunction
    {
        /// <summary>
        /// The slope of the function.
        /// </summary>
        private double m;

        /// <summary>
        /// The translation constant of the function.
        /// </summary>
        private double n;

        /// <summary>
        /// Initializes a new instance of the <see cref="LinearFunction"/> class.
        /// </summary>
        public LinearFunction()
        {
            this.m = 1;
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="LinearFunction"/> class.
        /// </summary>
        /// <param name="m">The slope of the function.</param>
        /// <param name="n">The translation constant of the function.</param>
        public LinearFunction(double m, double n)
        {
            this.m = m;
            this.n = n;
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="LinearFunction"/> class.
        /// </summary>
        /// <param name="function">The function to clone.</param>
        public LinearFunction(LinearFunction function)
        {
            if (function == (LinearFunction) null)
            {
                throw new ArgumentNullException("function");
            }
            if (function == (LinearFunction) null)
            {
                throw new ArgumentNullException("function");
            }

            this.m = function.Slope;
            this.n = function.TranslationConstant;
        }

        /// <summary>
        /// Gets or sets the slope of the function.
        /// </summary>
        /// <value>The slope of the function..</value>
        public double Slope
        {
            get { return m; }
            set { m = value; }
        }

        /// <summary>
        /// Gets or sets the translation constant of the function.
        /// </summary>
        /// <value>The translation constant of the function.</value>
        public double TranslationConstant
        {
            get { return n; }
            set { n = value; }
        }

        /// <summary>
        /// Gets the root of the linear function. If the function has no root the method 
        /// will throw an exception.
        /// </summary>
        /// <returns>The root of the linear function.</returns>
        public Double FindRoot()
        {
            if (m == 0)
            {
                throw new IllegalArithmeticException("Division by zero not allowed.");
            }

            return -(n / m);
        }

        /// <summary>
        /// Determines whether the function has a root.
        /// </summary>
        /// <returns>
        /// 	<c>true</c> if the function has a root; otherwise, <c>false</c>.
        /// </returns>
        public bool HasRoot()
        {
            return this.m != 0;
        }

        /// <summary>
        /// Determines whether the current linear function [is parallel to] [the specified compare function].
        /// </summary>
        /// <param name="compare">The compare function.</param>
        /// <returns>
        /// 	<c>true</c> if the current linear function [is parallel to] [the specified compare function]; otherwise, <c>false</c>.
        /// </returns>
        public bool IsParallelTo(LinearFunction compare)
        {
            if (compare == (LinearFunction)null)
                throw new ArgumentNullException("compare");

            return this.m == compare.Slope;
        }

        /// <summary>
        /// Determines whether the current linear function [is orthogonal to] [the specified compare function].
        /// </summary>
        /// <param name="compare">The compare function.</param>
        /// <returns>
        /// 	<c>true</c> if the current linear function [is orthogonal to] [the specified compare function]; otherwise, <c>false</c>.
        /// </returns>
        public bool IsOrthogonalTo(LinearFunction compare)
        {
            if (compare == (LinearFunction) null)
            {
                throw new ArgumentNullException("compare");
            }

            return this.m * compare.Slope == -1;
        }

        /// <summary>
        /// Transforms the current instance of a linear function to a polynomial.
        /// </summary>
        /// <returns>The transformed polynomial.</returns>
        public SimplePolynomial ToPolynomial()
        {
            return new SimplePolynomial(this.Slope, this.TranslationConstant);
        }

        /// <summary>
        /// Compares the current instance to another linear function.
        /// </summary>
        /// <param name="compare">The linear function to compare.</param>
        /// <returns>True if the functions are even otherwise, false.</returns>
        public bool CompareTo(LinearFunction compare)
        {
            return (this == compare);
        }

        /// <summary>
        /// Implements the operator +.
        /// </summary>
        /// <param name="a">The first linear function.</param>
        /// <param name="b">The second linear function.</param>
        /// <returns>The result of the operator.</returns>
        public static LinearFunction operator +(LinearFunction a, LinearFunction b)
        {
            if (a == (LinearFunction) null)
            {
                throw new ArgumentNullException("a");
            }

            if (b == (LinearFunction) null)
            {
                throw new ArgumentNullException("b");
            }

            return new LinearFunction(a.Slope + b.Slope, a.TranslationConstant + b.TranslationConstant);
        }

        /// <summary>
        /// Implements the operator -.
        /// </summary>
        /// <param name="a">The first linear function.</param>
        /// <param name="b">The second linear function.</param>
        /// <returns>The result of the operator.</returns>
        public static LinearFunction operator -(LinearFunction a, LinearFunction b)
        {
            if (a == (LinearFunction) null)
            {
                throw new ArgumentNullException("a");
            }

            if (b == (LinearFunction) null)
            {
                throw new ArgumentNullException("b");
            }

            return new LinearFunction(a.Slope - b.Slope, a.TranslationConstant - b.TranslationConstant);
        }

        /// <summary>
        /// Implements the operator ==.
        /// </summary>
        /// <param name="a">The first linear function.</param>
        /// <param name="b">The second linear function.</param>
        /// <returns>The result of the operator.</returns>
        public static bool operator ==(LinearFunction a, LinearFunction b)
        {
            if (System.Object.ReferenceEquals(a, b))
            {
                return true;
            }

            if (((object) a == null) || ((object) b == null))
            {
                return false;
            }

            return ((a.Slope == b.Slope) && (b.TranslationConstant == b.TranslationConstant));
        }

        /// <summary>
        /// Implements the operator !=.
        /// </summary>
        /// <param name="a">The first linear function.</param>
        /// <param name="b">The second linear function.</param>
        /// <returns>The result of the operator.</returns>
        public static bool operator !=(LinearFunction a, LinearFunction b)
        {
            return !(a == b);
        }

        /// <summary>
        /// Calculates the function f(x) at a specified x value.
        /// </summary>
        /// <param name="x">The specified x value.</param>
        /// <returns>The f(x) value at a specified x value.</returns>
        public double SolveAt(double x)
        {
            return this.m * x + this.n;
        }

        /// <summary>
        /// Determines whether the specified <see cref="T:System.Object"/> is equal to the current <see cref="T:System.Object"/>.
        /// </summary>
        /// <param name="obj">The <see cref="T:System.Object"/> to compare with the current <see cref="T:System.Object"/>.</param>
        /// <returns>
        /// true if the specified <see cref="T:System.Object"/> is equal to the current <see cref="T:System.Object"/>; otherwise, false.
        /// </returns>
        /// <exception cref="T:System.NullReferenceException">The <paramref name="obj"/> parameter is null.</exception>
        public override bool Equals(object obj)
        {
            return base.Equals(obj);
        }

        /// <summary>
        /// Serves as a hash function for a particular type.
        /// </summary>
        /// <returns>
        /// A hash code for the current <see cref="T:System.Object"/>.
        /// </returns>
        public override int GetHashCode()
        {
            return base.GetHashCode();
        }

        /// <summary>
        /// Returns a <see cref="T:System.String"/> that represents the current <see cref="T:System.Object"/>.
        /// </summary>
        /// <returns>
        /// A <see cref="T:System.String"/> that represents the current <see cref="T:System.Object"/>.
        /// </returns>
        public override string ToString()
        {
            return "f(x) = " + m.ToString() + "x + " + this.n;
        }
    }
}